#pragma once

#include "OCesium/TilingScheme.h"
#include "OCServer/MapCacher.h"

using namespace OCPP;
using namespace OCPP::Cesium;

#define DownloadChinaExtent 0

void downloadTDT();
void downloadWindy();
void downloadMlogcn();
void downloadArcgisPBF();
void downloadMaptilerPBF();
void downloadMapCacherHttp();
void downloadMapCacher();

auto getTileKey(std::shared_ptr<TilingScheme>& tilingScheme,float level)
{
    //int z = 4;
    //int minCol = 10, maxCol = 14;
    //int minRow = 4, maxRow = 7;

    int z = 4;
    int minCol = 11, maxCol = 14;
    int minRow = 5, maxRow = 7;
    
    auto rect1 = tilingScheme->tileXYToRectangle(minCol, minRow, z);
    auto rect2 = tilingScheme->tileXYToRectangle(maxCol, maxRow, z);

    rect1 = rect1.union_(rect1, rect2);

    uint32 maxIndexX = tilingScheme->getNumberOfXTilesAtLevel(level);
    uint32 maxIndexY = tilingScheme->getNumberOfYTilesAtLevel(level);

    auto tileXY1 = tilingScheme->positionToTileXY(Rectangle::southwest(rect1), level);
    auto tileXY2 = tilingScheme->positionToTileXY(Rectangle::northeast(rect1), level);
    int stCol = tileXY1.x;
    int edCol = tileXY2.x;
    int stRow = tileXY1.y;
    int edRow = tileXY2.y;
    if (stRow > edRow)
    {
        int tmp = edRow;
        edRow = stRow;
        stRow = tmp;
    }

    return std::make_tuple(stCol, stRow, edCol, edRow);
}

void MapDownload()
{
	OCPP_LOG_INFO << "MapDownload\n";
    downloadMapCacher();
}

void downloadMapCacher()
{
    std::vector<std::future<void> > tasks;

    String layer = "World_Hillshade";
    String url = "https://services.arcgisonline.com/arcgis/rest/services/Elevation/World_Hillshade/MapServer/tile/{z}/{y}/{x}.png";
    //String url = "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png";
    String cachePath = Stringify() << "G:/MapCacherMiss";
    MapCacher mapCacher(url, cachePath, "png", {});
    int totalLevel = 9;
    std::shared_ptr<TilingScheme> tilingScheme = std::make_shared<WebMercatorTilingScheme>();
    for (int z = 0; z <= totalLevel; z++)
    {
        int stX = 0, stY = 0;
        auto numX = tilingScheme->getNumberOfXTilesAtLevel(z);
        auto numY = tilingScheme->getNumberOfYTilesAtLevel(z);

#if DownloadChinaExtent
        auto extent = getTileKey(tilingScheme, z);
        stX = std::get<0>(extent);
        stY = std::get<1>(extent);
        numX = std::get<2>(extent);
        numY = std::get<3>(extent);
#endif // DownloadChinaExtent
        for (int y = stY; y < numY; y++)
        {
            for (int x = stX; x < numX; x++)
            {
                if (!mapCacher.exist(layer, z, y, x))
                {
                    
                    auto doWork = [&mapCacher,&layer](int z,int y,int x)
                        {
                            mapCacher.getCacheSync(layer, z, y, x);
                        };

                    tasks.push_back(std::async(doWork, z,y,x));

                    if (tasks.size() == 10)
                    {
                        for (auto& f : tasks)
                        {
                            f.get();
                        }
                        tasks.clear();
                    }
                }
            }
        }
    }

    for (auto& f : tasks)
    {
        f.get();
    }
    tasks.clear();
}

void downloadMapCacherHttp()
{
    const String baseUrl = "http://10.104.207.155:9080/MapServer/terrain-rgb";
    int totalLevel = 11;
    std::shared_ptr<TilingScheme> tilingScheme = std::make_shared<WebMercatorTilingScheme>();
    for (int z = 8; z <= totalLevel; z++)
    {
        int stX = 0, stY = 0;
        auto numX = tilingScheme->getNumberOfXTilesAtLevel(z);
        auto numY = tilingScheme->getNumberOfYTilesAtLevel(z);

#if DownloadChinaExtent
        auto extent = getTileKey(tilingScheme, z);
        stX = std::get<0>(extent);
        stY = std::get<1>(extent);
        numX = std::get<2>(extent);
        numY = std::get<3>(extent);
#endif // DownloadChinaExtent
        for (int y = stY; y < numY; y++)
        {
            for (int x = stX; x < numX; x++)
            {
                String path = Stringify() << "/" << z << "/" << y << "/" << x;
                String url = Stringify() << baseUrl << path;
                long lHTTPSCode;
                HttpClientEx httpClient([](const String& message)
                    {
                        OCPP_LOG_INFO << message << "\n";
                    });
                httpClient.SetTimeout(20);
                httpClient.fetchContent(url, lHTTPSCode);
                if (lHTTPSCode == 200)
                {
                    OCPP_LOG_INFO << path << "\n";
                }
            }
        }
    }
}

void downloadWindy()
{
    long lHTTPSCode;
    HttpClientEx httpClient([](const String& message)
        {
            OCPP_LOG_INFO << message << "\n";
        });
    httpClient.SetTimeout(10);

    std::shared_ptr<TilingScheme> tilingScheme = std::make_shared<WebMercatorTilingScheme>();

    String retina = "-retina";
#if 0
    retina = "";
#endif

    String outPath = "E:/darkmap" + retina;
    int totalLevel = 11;

    String baseUrl = "https://tiles.windy.com/tiles/v10.0/darkmap" + retina + "/";

    std::vector<std::future<void> > tasks;

    for (int z = 0; z <= totalLevel; z++)
    {
        int stX = 0, stY = 0;
        auto numX = tilingScheme->getNumberOfXTilesAtLevel(z);
        auto numY = tilingScheme->getNumberOfYTilesAtLevel(z);

#if DownloadChinaExtent
        auto extent = getTileKey(tilingScheme, z);
        stX = std::get<0>(extent);
        stY = std::get<1>(extent);
        numX = std::get<2>(extent);
        numY = std::get<3>(extent);
#endif // DownloadChinaExtent

        String zPath = Stringify() << outPath << "/" << z;
        FileUtil::makeDirectory(zPath);
        for (int y = stY; y < numY; y++)
        {
            String yPath = Stringify() << zPath << "/" << y;
            FileUtil::makeDirectory(yPath);
            for (int x = stX; x < numX; x++)
            {
                String name = Stringify() << x << ".png";
                String path = Stringify() << z << "/" << y << "/" << x;

                String saveFileName = joinPath(yPath, name);

                if (FileUtil::existFile(saveFileName)) continue;

                String url = Stringify() << baseUrl << z << "/" << x << "/" << y << ".png";
               
                auto doWork = [](String fileName, String url)
                    {
                        long lHTTPSCode;
                        HttpClientEx httpClient([](const String& message)
                            {
                                OCPP_LOG_INFO << message << "\n";
                            });
                        httpClient.SetTimeout(10);
                        bool result = httpClient.DownloadFile(fileName, url, lHTTPSCode);
                        if (!result)
                        {
                            OCPP_LOG_ERROR << url << " fail.\n";
                        }
                        else
                        {
                            OCPP_LOG_INFO << url << " fetched.\n";
                        }
                    };

                tasks.push_back(std::async(doWork, saveFileName, url));

                if (tasks.size() == 10)
                {
                    for (auto& f : tasks)
                    {
                        f.get();
                    }
                    tasks.clear();
                }
            }
        }
    }

    for (auto& f : tasks)
    {
        f.get();
    }
    tasks.clear();
}

void downloadMlogcn()
{
    long lHTTPSCode;
    HttpClientEx httpClient([](const String& message)
        {
            OCPP_LOG_INFO << message << "\n";
        });
    httpClient.SetTimeout(10);

    std::shared_ptr<TilingScheme> tilingScheme = std::make_shared<WebMercatorTilingScheme>();

    String outPath = "F:/mlogcn";
    int totalLevel = 9;

    String baseUrl = "http://gfapi.mlogcn.com/visLayer/v001/terrain?level={z}&x={x}&y={y}&key=1mfZDdhGQ2cPqz9req3wAUUVTnU1CMPQ";

    std::vector<std::future<void> > tasks;

    for (int z = 2; z <= totalLevel; z++)
    {
        int stX = 0, stY = 0;
        auto numX = tilingScheme->getNumberOfXTilesAtLevel(z);
        auto numY = tilingScheme->getNumberOfYTilesAtLevel(z);

#if DownloadChinaExtent
        auto extent = getTileKey(tilingScheme, z);
        stX = std::get<0>(extent);
        stY = std::get<1>(extent);
        numX = std::get<2>(extent);
        numY = std::get<3>(extent);
#endif // DownloadChinaExtent

        String zPath = Stringify() << outPath << "/" << z;
        FileUtil::makeDirectory(zPath);
        for (int y = stY; y < numY; y++)
        {
            String yPath = Stringify() << zPath << "/" << y;
            FileUtil::makeDirectory(yPath);
            for (int x = stX; x < numX; x++)
            {
                String name = Stringify() << x << ".png";
                String path = Stringify() << z << "/" << y << "/" << x;

                String saveFileName = joinPath(yPath, name);

                if (FileUtil::existFile(saveFileName)) continue;

                String url = StringUtil::replaceAll(baseUrl, "{z}", Stringify() << z);
                url = StringUtil::replaceAll(url, "{y}", Stringify() << y);
                url = StringUtil::replaceAll(url, "{x}", Stringify() << x);

                auto doWork = [](String fileName, String url)
                    {
                        long lHTTPSCode;
                        HttpClientEx httpClient([](const String& message)
                            {
                                OCPP_LOG_INFO << message << "\n";
                            });
                        httpClient.SetTimeout(10);
                        bool result = httpClient.fetchFile(url, fileName);
                        if (!result)
                        {
                            OCPP_LOG_ERROR << url << " fail.\n";
                        }
                        else
                        {
                            OCPP_LOG_INFO << url << " fetched.\n";
                        }
                    };

                tasks.push_back(std::async(doWork, saveFileName, url));

                if (tasks.size() == 10)
                {
                    for (auto& f : tasks)
                    {
                        f.get();
                    }
                    tasks.clear();
                }
            }
        }
    }

    for (auto& f : tasks)
    {
        f.get();
    }
    tasks.clear();
}

void downloadTDT()
{
    HttpClient::UserAgent = "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.48 Safari/537.36";
    HttpClient::Referer = "www.tianditu.gov.cn";
    long lHTTPSCode;
    HttpClientEx httpClient([](const String& message)
        {
            OCPP_LOG_INFO << message << "\n";
        });

    httpClient.AddHeader("Accept-Encoding: gzip, deflate, br");
    httpClient.AddHeader("Cookie: HWWAFSESID = 30ecac403cf83cb34c9; HWWAFSESTIME = 1698832236619");

    String url = "https://t2.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILECOL=11&TILEROW=5&TILEMATRIX=4&tk=0b79a07d2808103ab84aa56485c331a8";
    String fileName = "E:/1.jpg";
    bool result = httpClient.DownloadFile(fileName, url, lHTTPSCode);
    if (result)
    {
        OCPP_LOG_INFO << url << " fetched.\n";
    }
}

void downloadArcgisPBF()
{
    //String baseUrl = "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/4/6/13.pbf";
    String baseUrl = "https://basemaps.arcgis.com/arcgis/rest/services/World_Basemap_v2/VectorTileServer/tile/{z}/{y}/{x}.pbf";
    long lHTTPSCode;
    HttpClientEx httpClient([](const String& message)
        {
            OCPP_LOG_INFO << message << "\n";
        });
    httpClient.SetTimeout(10);

    std::shared_ptr<TilingScheme> tilingScheme = std::make_shared<WebMercatorTilingScheme>();

    String outPath = "F:/World_Basemap_v2";
    int totalLevel = 14;

    std::vector<std::future<void> > tasks;

    for (int z = 14; z <= totalLevel; z++)
    {
        int stX = 0, stY = 0;
        auto numX = tilingScheme->getNumberOfXTilesAtLevel(z);
        auto numY = tilingScheme->getNumberOfYTilesAtLevel(z);

#if DownloadChinaExtent
        auto extent = getTileKey(tilingScheme, z);
        stX = std::get<0>(extent);
        stY = std::get<1>(extent);
        numX = std::get<2>(extent);
        numY = std::get<3>(extent);
#endif // DownloadChinaExtent

        String zPath = Stringify() << outPath << "/" << z;
        FileUtil::makeDirectory(zPath);
        for (int y = stY; y < numY; y++)
        {
            String yPath = Stringify() << zPath << "/" << y;
            FileUtil::makeDirectory(yPath);
            for (int x = stX; x < numX; x++)
            {
                String name = Stringify() << x << ".pbf";
                String path = Stringify() << z << "/" << y << "/" << x;

                String saveFileName = joinPath(yPath, name);

                if (FileUtil::existFile(saveFileName)) continue;

                String url = StringUtil::replaceAll(baseUrl, "{z}", Stringify() << z);
                url = StringUtil::replaceAll(url, "{y}", Stringify() << y);
                url = StringUtil::replaceAll(url, "{x}", Stringify() << x);

                auto doWork = [](String fileName, String url)
                    {
                        long lHTTPSCode;
                        HttpClientEx httpClient([](const String& message)
                            {
                                OCPP_LOG_INFO << message << "\n";
                            });
                        httpClient.SetTimeout(10);
                        bool result = httpClient.fetchFile(url, fileName);
                        if (!result)
                        {
                            OCPP_LOG_ERROR << url << " fail.\n";
                        }
                        else
                        {
                            OCPP_LOG_INFO << url << " fetched.\n";
                        }
                    };

                tasks.push_back(std::async(doWork, saveFileName, url));

                if (tasks.size() == 10)
                {
                    for (auto& f : tasks)
                    {
                        f.get();
                    }
                    tasks.clear();
                }
            }
        }
    }

    for (auto& f : tasks)
    {
        f.get();
    }
    tasks.clear();
}

void downloadMaptilerPBF()
{
    String baseUrl1 = "https://api.maptiler.com/tiles/v3/{z}/{x}/{y}.pbf?key=pSHUA9sSkNny3iqoWG4P";
    String baseUrl2 = "https://api.maptiler.com/tiles/v3/{z}/{x}/{y}.pbf?key=eIgS48TpQ70m77qKYrsx";

    long lHTTPSCode;
    HttpClientEx httpClient([](const String& message)
        {
            OCPP_LOG_INFO << message << "\n";
        });
    httpClient.SetTimeout(10);

    std::shared_ptr<TilingScheme> tilingScheme = std::make_shared<WebMercatorTilingScheme>();

    String outPath = "F:/maptiler";
    int totalLevel = 11;

    std::vector<std::future<void> > tasks;

    for (int z = 2; z <= totalLevel; z++)
    {
        int stX = 0, stY = 0;
        auto numX = tilingScheme->getNumberOfXTilesAtLevel(z);
        auto numY = tilingScheme->getNumberOfYTilesAtLevel(z);

#if DownloadChinaExtent
        auto extent = getTileKey(tilingScheme, z);
        stX = std::get<0>(extent);
        stY = std::get<1>(extent);
        numX = std::get<2>(extent);
        numY = std::get<3>(extent);
#endif // DownloadChinaExtent

        String zPath = Stringify() << outPath << "/" << z;
        FileUtil::makeDirectory(zPath);
        for (int y = stY; y < numY; y++)
        {
            String yPath = Stringify() << zPath << "/" << y;
            FileUtil::makeDirectory(yPath);
            for (int x = stX; x < numX; x++)
            {
                String name = Stringify() << x << ".pbf";
                String path = Stringify() << z << "/" << y << "/" << x;

                String saveFileName = joinPath(yPath, name);

                if (FileUtil::existFile(saveFileName)) continue;
                String baseUrl = x % 2 == 0 ? baseUrl1 : baseUrl2;
                String url =StringUtil::replaceAll(baseUrl1, "{z}", Stringify() << z);
                url = StringUtil::replaceAll(url, "{y}", Stringify() << y);
                url = StringUtil::replaceAll(url, "{x}", Stringify() << x);

                auto doWork = [](String fileName, String url)
                    {
                        long lHTTPSCode;
                        HttpClientEx httpClient([](const String& message)
                            {
                                OCPP_LOG_INFO << message << "\n";
                            });
                        httpClient.SetTimeout(10);
                        bool result = httpClient.fetchFile(url, fileName);
                        if (!result)
                        {
                            OCPP_LOG_ERROR << url << " fail.\n";
                        }
                        else
                        {
                            OCPP_LOG_INFO << url << " fetched.\n";
                        }
                    };

                tasks.push_back(std::async(doWork, saveFileName, url));

                if (tasks.size() == 10)
                {
                    for (auto& f : tasks)
                    {
                        f.get();
                    }
                    tasks.clear();
                }
            }
        }
    }

    for (auto& f : tasks)
    {
        f.get();
    }
    tasks.clear();

}